/*
 * Data Explorer: Main Control File
 *      control.js
 *
 * last modified: 3/24/11
 * author: James Duran
 *
 * description: Serves as the primary control interface
 *              for the visual interface. Provides functions
 *              for major system operations, and references
 *              various php files within certain functions.
 *
 * comments: n/a
 */

// nodeArray will contain all map element nodes,
// every time a new query is made the array will be cleared
// and all nodes reset.
var nodeArray = new Array();
var infoWindowArray = new Array();

// primary javascript map object
var map;
var formattedAddress = "empty";

// dataset class helps keep track of name across accordion selections
function Dataset() {this.name = "empty";
                    this.currentJSONObject = null;
                    this.searchTagArray = null;
                    this.code = null;
                    this.title = "empty";
                    this.emptyResult = true;}
var ds = new Dataset();

// FAAJSONQuery:
//
// uses JSONQuery to create a json query meant to specifically query the FAA
// web service. Includes a callback to perform the rest of the search.
function FAAJSONQuery(airportCode, latitude, longitude, code, title) {
    JSONQuery("http://services.faa.gov/airport/status/" + airportCode,
                  function(json){
                        ds.currentJSONObject = json;
                        showProgress(false, false);
                        setNotifyArea("Result has been retrieved from the FAA Web Service");
                        createFAAWebServiceResult(ds.currentJSONObject,
                                                  latitude, longitude, code, title);
                        $("#results").show();
		  }, "?format=json&callback=_jqjsp");
}

// FAAJSONProximitySearch:
//
// perfoms a geocode proximity search. Constructs a json (not jsonp) query based off global
// values within the ds variable, and creates a callback within the json query that handles
// the generation and link behaviors of markers on the google map element.
function FAAJSONProximitySearch() {
    // get the distance and code values from the proximity element and global json object
    var _dist = $("#proximityValues").val();
    var _code = ds.currentJSONObject.IATA;

    // empty, then recreate the proximity search element
    // this helps keep the results clean and free from duplicates
    $("#proximitySearchText").empty();
    createProximitySearchText();
    $.getJSON(PROXIMITY_SEARCH + "?code=" + _code + "&dist=" + _dist,
              function(data) {
                 var _marker;
                 // resets all map marker nodes, if any previously existed
                 unsetMapNodes(nodeArray);
                 for(i in data) {
                     var _tmp = data[i].value.split(", ");
                     //create a new latlng variable, pointing to geocodes from result
                     var _latlng = new google.maps.LatLng(parseFloat(data[i].id.latitude),
                                                          parseFloat(data[i].id.longitude));
                     // create a new node pointing to _latlng geocode
                     if(i == 0)
                         _marker = createHomeIconMarker();
                     else
                         _marker = createNumericalIconMarker(i);

                     nodeArray.push(new google.maps.Marker({icon: _marker, position: _latlng,
			                       title: _tmp[0] + ", " +_tmp[1]}));
                     // set it to the map
                     nodeArray[i].setMap(map);
                     var _node = nodeArray[i];
		     google.maps.event.addListener(_node, 'click', function() {
                             // perform a search based on the retrieved marker node
			     performFAASearch("dynamic", this);
			 });
                     if(i == 0) {
                        map.setCenter(_latlng);
                        $("#proximitySearchText").append("<br/><br/><b style='float: left;'>From " + _tmp[0] + ", " + _tmp[1] + ":</b><div style='float: right'>(" + _dist + " miles)</div><hr style='clear: both;'/>");
                     } else
                        $("#proximitySearchText").append(i + ". <a href='" + getRootURL() + "?ds=faaws&q=" + _tmp[0] + "'>" + _tmp[0] + ", " + _tmp[1] + "</a><div style='float: right;'>(" + data[i].id.distance + ")</div><div style='clear: both;'></div>");
                 }

                 // set zoom level based on distance value
                 if(_dist == 100 && data.length > 1)autoCenterNodes();
                    //map.setZoom(8);
                 else if(_dist == 50 && data.length > 1)autoCenterNodes();
                    //map.setZoom(8);
                 else if(_dist == 25 && data.length > 1)autoCenterNodes();
                    //map.setZoom(10);
                 else {
                    $("#proximitySearchText").append("(No nearby airports found within " +
                                                      _dist + " miles.)");
                    map.setZoom(13);
                 }
              });
}

// USGSJSONProximitySearch:
//
// perfoms a geocode proximity search. Constructs a json (not jsonp) query based off global
// values within the ds variable, and creates a callback within the json query that handles
// the generation and link behaviors of markers on the google map element.
function USGSJSONProximitySearch() {
    // get the distance and code values from the proximity element and global json object
    var _dist = $("#proximityValues").val();
    var _code = ds.searchTagArray.id.site_id;

    // empty, then recreate the proximity search element
    // this helps keep the results clean and free from duplicates
    $("#proximitySearchText").empty();
    createProximitySearchText();
    $.getJSON(PROXIMITY_SEARCH + "?ds=usgs&code=" + _code + "&dist=" + _dist,
              function(data) {
                 var _marker;
                 // resets all map marker nodes, if any previously existed
                 unsetMapNodes(nodeArray);
                 for(i in data) {
                     var _tmp = data[i].value.name.split(", ");
                     //create a new latlng variable, pointing to geocodes from result
                     var _latlng = new google.maps.LatLng(parseFloat(data[i].id.latitude),
                                                          parseFloat(data[i].id.longitude));
                     // create a new node pointing to _latlng geocode
                     if(i == 0)
                         _marker = createHomeIconMarker();
                     else
                         _marker = createNumericalIconMarker(i);

                     nodeArray.push(new google.maps.Marker({icon: _marker, position: _latlng,
			                       title: data[i].value.code + ", " +_tmp[0]}));
                     // set it to the map
                     nodeArray[i].setMap(map);
                     var _node = nodeArray[i];
		     google.maps.event.addListener(_node, 'click', function() {
                             // perform a search based on the retrieved marker node
			     USGSWebQuery("dynamic", this);
			 });
                     if(i == 0) {
                        map.setCenter(_latlng);
                        $("#proximitySearchText").append("<br/><br/><b style='float: left;'>From " + _tmp[0] + ", " + _tmp[1] + ":</b><div style='float: right'>(" + _dist + " miles)</div><hr style='clear: both;'/>");
                     } else
                        $("#proximitySearchText").append(i + ". <a href='" + getRootURL() + "?ds=usgs&q=" + data[i].value.code + "'>" + _tmp[0] + ", " + _tmp[1] + "</a><div style='float: right;'>(" + data[i].id.distance + ")</div><div style='clear: both;'></div>");
                 }

                 // set zoom level based on distance value
                 if(_dist == 100 && data.length > 1)autoCenterNodes();
                    //map.setZoom(8);
                 else if(_dist == 50 && data.length > 1)autoCenterNodes();
                    //map.setZoom(8);
                 else if(_dist == 25 && data.length > 1)autoCenterNodes();
                    //map.setZoom(10);
                 else {
                    $("#proximitySearchText").append("(No nearby sites found within " +
                                                      _dist + " miles.)");
                    map.setZoom(13);
                 }
              });
}

// enableRadioAddons:
//
// enable all additional radio elements besides radio1 aka "Map"
// and set their titles.
function enableRadioAddons(isEnabled, radio2Title, radio3Title) {
    var _r2 = "<span class='ui-button-text'>" + radio2Title + "</span>";
    var _r3 = "<span class='ui-button-text'>" + radio3Title + "</span>";

    if(isEnabled == true) {
	$("#radio2").button("enable");
	$("#radio3").button("enable");
	$("label[for=radio2], #radio2L").html(_r2);
	$("label[for=radio3], #radio3L").html(_r3);
    } else {
	$("label[for=radio2], #radio2L").html(_r2);
	$("label[for=radio3], #radio3L").html(_r3);
	$("#radio2").button("disable");
	$("#radio3").button("disable");
    }
}

// performFAASearch:
//
// performs an FAA web service search based off the from URL and ui parameters.
// fromURL == true: the FAA search was initiated from the URL directly.
// fromURL == "dynamic": the FAA search was initiated by clicking a map marker.
// fromURL == false: the FAA web search was initiated by the main search box.
function performFAASearch(fromURL, ui) {
        var _strArray;
	var _lat, _lng, _code, _title;

        if(fromURL == true) {
            var _ss = ds.searchTagArray[0].value;
            _strArray = _ss.split(", ");
            ds.searchTagArray = ui[0];
	    _lat = ui[0].id.latitude;
	    _lng = ui[0].id.longitude;
	    _code = _strArray[0];
            ds.code = _code; // fix to allow radio element support
	    _title = _strArray[1];
        } else if(fromURL == "dynamic") {
	    _lat = ui.position.lat();
	    _lng = ui.position.lng();
	    _strArray = ui.title.split(", ");
	    _code = _strArray[0];
            ds.code = _code; // fix to allow radio element support
	    _title = _strArray[1];
            // automatically fill in the search text box
            $("#tags").attr({value: _code + ", " + _title});
	} else {
            _strArray = ui.item.value.split(", ");
            ds.searchTagArray = ui.item;
	    _lat = ui.item.id.latitude;
	    _lng = ui.item.id.longitude;
	    _code = _strArray[0];
            ds.code = _code; // fix to allow radio element support
	    _title = _strArray[1];
        }

        // resets all map marker nodes, if any previously existed
        unsetMapNodes(nodeArray);
        //create a new latlng variable, pointing to geocodes from result
        var _latlng = new google.maps.LatLng(parseFloat(_lat),
                                             parseFloat(_lng));
        // create a new node pointing to _latlng geocode
        nodeArray.push(new google.maps.Marker({position: _latlng,
			                       title: _title}));
        // set it to the map
        nodeArray[nodeArray.length - 1].setMap(map);
        map.setCenter(_latlng);
        map.setZoom(13);

	$("#tabs").tabs("select", 1);
        $("#results").hide();
        showProgress(true, true, "<p class='ui-state-highlight ui-corner-all'>" +
                     "<span class='ui-icon ui-icon-info' style='float: left; margin-right: 0.3em;'>" +
                     "</span>Retrieving data result from the FAA Web Service...</p>");

        geocodeGetAddress(_lat, _lng);
	FAAJSONQuery(_code, _lat, _lng, _code, _title);
	// enable radio buttons and set their respective titles
	enableRadioAddons(true, "Summary", "Hourly Delays");
	FAARadio2XMLQuery();
	FAARadio3XMLQuery();
}

// keyDragZoomEvent:
//
// @param bnds - LatLngBounds
// represents the main event handler for the keydragzoom map control.
// Called upon the trigger of the map control event 'dragend'.
function keyDragZoomEvent(bnds) {
    // get boundry information
    var _sw_lat = bnds.getSouthWest().lat(), _sw_lng = bnds.getSouthWest().lng();
    var _ne_lat = bnds.getNorthEast().lat(), _ne_lng = bnds.getNorthEast().lng();

    // select the result tab and display the loading icon
    $("#tabs").tabs("select", 1);
    $("#results").hide();
    showProgress(true, true, "<p class='ui-state-highlight ui-corner-all'>" +
                 "<span class='ui-icon ui-icon-info' style='float: left; margin-right: 0.3em;'>" +
                 "</span>Searching the " + ds.name + "...</p>");

    // perform rectangular window search, and retrieve json result
    $.getJSON(PROXIMITY_SEARCH + "?ds=" + getDatasetShortCode(ds.name) +
                "&sw_lat=" + _sw_lat + "&sw_lng=" + _sw_lng +
                "&ne_lat=" + _ne_lat + "&ne_lng=" + _ne_lng,
                function(json) {
                    showProgress(false, false);
                    setNotifyArea("Result has been retrieved from the " + ds.name + ".");
                    createDragZoomResult(json);
                });
}

// initialize:
//
// initialize google maps javascript element, and center the map based
// on latitude/longitude (grand island, ne). initialize is called by
// <body onload="initialize()">.
function initialize() {
    // the following location is approximately centered in the US map.
    var latlng = new google.maps.LatLng(40.924740, -98.339904);
    var myOptions = {zoom: 4, center: latlng,
                     mapTypeId: google.maps.MapTypeId.HYBRID};
    var _ds = $.getURLVar('ds'), _q = $.getURLVar('q');
    map = new google.maps.Map(document.getElementById("map_canvas"),
                              myOptions);
    map.enableKeyDragZoom({boxStyle: {border: "4px solid #7693D3"}});
    var dz = map.getDragZoomObject();
    google.maps.event.addListener(dz, "dragend", keyDragZoomEvent);

    // parse any attributes that are present in the URL string
    if( _ds == undefined || _q == undefined)
        setNotifyArea("No search attributes are assigned within the URL.");
    else {
	if(_ds == "faaws") {
	    $("#accordion").accordion("activate", 1);
	    setNotifyArea("Querying " + _ds + " for " + _q + ".");
	    $.ajax({type: "GET",
                url: SEARCH_QUERY + "?ds=faaws&q=" + _q,
                success: function(data) {
                    setNotifyArea("Result tag retrieved.");
                    ds.searchTagArray = data;
                    $("#tags").attr({value: ds.searchTagArray[0].value});
                    performFAASearch(true, data);
                },
                dataType: "json"
               });
	}
	if(_ds == "usgs") {
	    $("#accordion").accordion("activate", 2);
	    setNotifyArea("Querying " + _ds + " for " + _q + ".");
	    $.ajax({type: "GET",
			url: SEARCH_QUERY + "?ds=usgs&q=" + _q,
			success: function(data) {
			  setNotifyArea("Result tag retrieved.");
			  ds.searchTagArray = data[0];
			  $("#tags").attr({value: ds.searchTagArray.value});
			  USGSWebQuery(true, ds.searchTagArray);
		        },
			dataType: "json"
			});
	}
    }
}

// autoCompleteSelectEvent:
//
// called after a value is selected in the autocomplete textbox.
function autoCompleteSelectEvent(event, ui) {
    //if the FAA Web Service is selected
    if(ds.name == "FAA Web Service") {
	performFAASearch(false, ui);
    }
    // if the USGS Web Service is selected
    if(ds.name == "USGS Water Quality Web Service") {
	USGSWebQuery(false, ui);
    }
}

// USGSWebQuery:
//
// performs a retireval for USGS XML data based on the site id of the
// search entry's JSON tag.
function USGSWebQuery(fromURL, ui) {
        var _strArray;
	var _lat, _lng, _code, _title;

	if(fromURL == false) {
            _strArray = ui.item.value.split(",");
            ds.searchTagArray = ui.item;
	    _lat = ui.item.id.latitude;
	    _lng = ui.item.id.longitude;
	    _code = ui.item.id.site_id;
            ds.code = _code; // fix to allow radio element support
	    ds.title = _title = _strArray[0]; // fix to allow title string to be included into the html result element
	} else if(fromURL == "dynamic") {
	    _lat = ui.position.lat();
	    _lng = ui.position.lng();
	    _strArray = ui.title.split(",");
	    _code = _strArray[0];
	    ds.code = _code;
	    ds.title = _title = _strArray[1];
	    $("#tags").attr({value: _code + ", " + _title});
	} else {
	    _strArray = ui.value.split(",");
	    _lat = ui.id.latitude;
	    _lng = ui.id.longitude;
	    _code = ui.id.site_id;
	    ds.searchTagArray = ui;
	    ds.code = _code;
	    ds.title = _title = _strArray[0];
	}

        // resets all map marker nodes, if any previously existed
        unsetMapNodes(nodeArray);
        //create a new latlng variable, pointing to geocodes from result
        var _latlng = new google.maps.LatLng(parseFloat(_lat),
                                             parseFloat(_lng));
        // create a new node pointing to _latlng geocode
        nodeArray.push(new google.maps.Marker({position: _latlng,
			                       title: _title}));
        // set it to the map
        nodeArray[nodeArray.length - 1].setMap(map);
        map.setCenter(_latlng);
        map.setZoom(13);

	$("#tabs").tabs("select", 1);
        $("#results").hide();
        showProgress(true, true, "<p class='ui-state-highlight ui-corner-all'>" +
                     "<span class='ui-icon ui-icon-info' style='float: left; margin-right: 0.3em;'>" +
                     "</span>Retrieving data result from the USGS Web Service...</p>");

        geocodeGetAddress(_lat, _lng);
	XMLQuery(ADDITIONAL_SOURCES + "?ds=usgs&q=" + _code, function(xml) {
		showProgress(false, false);
		setNotifyArea("Result has been retrieved from the USGS Web Service.");
		createUSGSWebServiceResult(xml, ds.searchTagArray.id.latitude,
					   ds.searchTagArray.id.longitude, ds.code,
					   ds.title, ds.searchTagArray.id.huc_code);
		$("#results").show();
	});
}

// searchButtonSelectEvent:
//
// called when the search button is selected.
function searchButtonSelectEvent() {
    var _query = $("#tags").val();
    if(ds.name == "FAA Web Service" && _query != "") {
	setNotifyArea("Querying " + ds.name + " for " + _query + ".");
	$.ajax({type: "GET",
                url: SEARCH_TAGS + "?ds=" + ds.name + "&term=" + _query,
                success: function(data) {
                    setNotifyArea("Result tag retrieved.");
                    ds.searchTagArray = data;
		    if(ds.searchTagArray[0] == undefined) {
			showNoResultErrorDialog();
		    } else {
			$("#tags").val(ds.searchTagArray[0].value);
			performFAASearch(true, data);
		    }
                },
                dataType: "json"
               });
    } else {
	alert("Please select a dataset and enter a search query.");
    }
}

// datasetChangeEvent:
//
// called whenever an accordion element is changed.
function datasetChangeEvent(event, ui) {
    ds.name = ui.newHeader.find("a").text();
    setNotifyArea(ds.name + " is now active.");
    $("#tags").autocomplete({source: SEARCH_TAGS + "?ds=" + ds.name,
                             minLength: 2,
		             select: autoCompleteSelectEvent
		            });
    enableRadioAddons(false, "*", "*");
}

// radio1ClickHandler:
//
// click event handler for the radio element radio1.
function radio1ClickHandler() {
    $("#radio2_canvas").hide();
    $("#radio3_canvas").hide();
    $("#map_canvas").show();
}

// FAARadio2XMLQuery:
//
// creates a specialized xml query meant to gather a summary of
// flight delays from the flyontime api.
function FAARadio2XMLQuery() {
    $("#radio2_canvas").html("Retrieving historical delay data...");
    // make a xml get query to the additional sources php file
    XMLQuery(ADDITIONAL_SOURCES + "?ds=faaws&q=" + ds.code, function(xml) {
	    $(xml).find("summary").each(function() {
		    $("#radio2_canvas").empty();
                    createDelaySummaryPieChart(ds.code,
                                               $(this).find("count").text(),
                                               $(this).find("on_time").text(),
                                               $(this).find("short_delay").text(),
                                               $(this).find("long_delay").text(),
                                               $(this).find("cancelled").text(),
                                               "radio2_canvas");

		    // if the radio element is selected then make sure the canvas div
		    // is visible, else simply hide it and its content.
		    if($("#radio2_canvas").is(":visible"))
			$("#radio2_canvas").show();
		    else
			$("#radio2_canvas").hide();
		});
	});
}

// FAARadio3XMLQuery:
//
// creates a specialized xml query meant to gather a summary of
// flight delays by hour from the flyontime api.
function FAARadio3XMLQuery() {
    $("#radio3_canvas").html("Retrieving historical delay data...");
    // make a xml get query to the additional sources php file
    XMLQuery(ADDITIONAL_SOURCES + "?ds=faaws&q=" + ds.code, function(xml) {
	    var _onTime = new Array();
	    var _shortDelay = new Array();
	    var _longDelay = new Array();
	    var _cancelled = new Array();
	    var _hour = new Array();
	    $(xml).find("time").each(function() {
		    _onTime.push(Math.round($(this).find("on_time").text()));
		    _shortDelay.push(Math.round($(this).find("short_delay").text()));
		    _longDelay.push(Math.round($(this).find("long_delay").text()));
		    _cancelled.push(Math.round($(this).find("cancelled").text()));
		    _hour.push(convertTo12Hour($(this).find("hour").text()));
		});
	    $("#radio3_canvas").empty();
	    createDelayByHourBarChart(ds.code, _onTime,
				      _shortDelay, _longDelay,
				      _cancelled, _hour, "radio3_canvas");
	    // if the radio element is selected then make sure the canvas div
	    // is visible, else simply hide it and its content.
	    if($("#radio3_canvas").is(":visible"))
		$("#radio3_canvas").show();
	    else
		$("#radio3_canvas").hide();
	    _onTime.length = 0;_shortDelay.length = 0;
	    _longDelay.length = 0;_cancelled.length = 0;_hour.length = 0;
	});
}

// radio2ClickHandler:
//
// click event handler for the radio element radio2.
function radio2ClickHandler() {
    $("#map_canvas").hide();
    $("#radio3_canvas").hide();
    $("#radio2_canvas").show();

    // fix: now thinking about it, performing the external xml query
    // within the performFAASearch function offers faster performance,
    // as the image does not have to be re-loaded each time a radio element
    // is activated.
    if(ds.name == "FAA Web Service") {
	//$("#radio2_canvas").html("Retrieving historical delay data...");
        //FAARadio2XMLQuery();
    } else {
	$("#radio2_canvas").html("No dataset is currently selected.");
    }
}

// radio3ClickHandler:
//
// click event handler for the radio element radio3.
function radio3ClickHandler() {
    $("#map_canvas").hide();
    $("#radio2_canvas").hide();
    $("#radio3_canvas").show();

    if(ds.name == "FAA Web Service") {
	//$("#radio3_canvas").html("Retrieving historical delay data...");
    } else {
	$("#radio3_canvas").html("No dataset is currently selected.");
    }
}

// un-named function that initializes most of the jquery ui elements,
// attaching them to div id elements within the html.
$(function(){
    $("#tabs").tabs();
    $("#accordion").accordion({changestart: datasetChangeEvent});
    $("button, input:submit").button();
    $("#radio").buttonset();
    $("#radio1").click(function() {radio1ClickHandler();});
    $("#radio2").click(function() {radio2ClickHandler();});
    $("#radio3").click(function() {radio3ClickHandler();});
    enableRadioAddons(false, "*", "*");

    // fix: make sure (especially for internet explorer) that the map
    // radio element (aka radio1) is shown while the rest remain hidden
    radio1ClickHandler();
    $("#progressBar").html("<img style='display: block; margin-left: auto; margin-right: auto;' src='img/ajax-loader.gif' alt='Loading...' />").hide();
    createErrorDiv("Please select a dataset and enter a search query to see a result.");
});
